package org.example.mq.mqclient;

import org.example.mq.common.*;

import java.io.*;
import java.net.Socket;
import java.net.SocketException;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created with IntelliJ IDEA.
 * Description:做渐进主义的改良派,不做完没注意的速成派
 * User: XALEXC
 * Date: 2025-06-25
 * Time: 16:32
 */
public class Connection {
    private Socket socket=null;

    private ConcurrentHashMap<String,Channel>channelMap=new ConcurrentHashMap<>();

    private InputStream inputStream;
    private OutputStream outputStream;
    private DataInputStream dataInputStream;
    private DataOutputStream dataOutputStream;
    private ExecutorService callbackPool= null;

    public Connection(String host, int port) throws IOException {
        socket = new Socket(host,port);
        inputStream=socket.getInputStream();
        outputStream=socket.getOutputStream();
        dataInputStream=new DataInputStream(inputStream);
        dataOutputStream=new DataOutputStream(outputStream);
        callbackPool=Executors.newFixedThreadPool(4);

        // 创建一个扫描线程,负责不停的从 socket 中读取响应数据.把这个响应数据再交给对应的channel负责处理
        Thread t=new Thread(()->{
           try{
               while(!socket.isClosed()){
                   Response response=readResponse();
                   dispatchResponse(response);
               }
           }catch (SocketException e){
               // 连接正常断开,此时这个异常直接忽略
               System.out.println("[Connection] 连接正常断开!");
               e.printStackTrace();
           }catch (MqException|ClassNotFoundException|IOException e){
               System.out.println("[Connection] 连接异常断开");
               e.printStackTrace();
           }
        });
        t.start();
    }

    public void close(){
        try{
            callbackPool.shutdownNow();
            channelMap.clear();
            inputStream.close();
            outputStream.close();
            socket.close();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
    // 使用这个方法来分别处理,当前的响应是一个针对控制请求的响应,还是服务器推送的消息.
    private void dispatchResponse(Response response) throws IOException, ClassNotFoundException, MqException {
        if (response.getType()==0xc){
            // 服务器推送来的数据
            SubScribeReturns subScribeReturns= (SubScribeReturns) BinaryTool.fromBytes(response.getPayload());
            // 根据 channelId 来找到对应的 channel 对象
            Channel channel=channelMap.get(subScribeReturns.getChannelId());
            if (channel==null){
                throw new MqException("[Connection] 该消息对应的channel在客户端中不存在!channelId="
                +channel.getChannelId());
            }
            // 执行该 channel 对象的回调
            callbackPool.submit(()->{
                try {
                    channel.getConsumer().handleDelivery(subScribeReturns.getConsumerTag(),
                            subScribeReturns.getBasicProperties(), subScribeReturns.getBody());
                } catch (MqException | IOException e) {
                    e.printStackTrace();
                }
            });
        }else{
            // 当前响应是针对刚才的控制请求的响应
            BasicReturns basicReturns= (BasicReturns) BinaryTool.fromBytes(response.getPayload());
            // 把结果放到对应的Map中
            Channel channel=channelMap.get(basicReturns.getChannelId());
            if (channel==null){
                throw new MqException("[Connection] 该消息对应的channel在客户端中不存在!channelId="
                        +channel.getChannelId());
            }
            channel.putReturns(basicReturns);
        }
    }

    // 发送请求
    public void writeRequest(Request request) throws IOException {
        dataOutputStream.writeInt(request.getType());
        dataOutputStream.writeInt(request.getLength());
        dataOutputStream.write(request.getPayload());
        dataOutputStream.flush();
        System.out.println("[Connection] 发送请求! type="+request.getType()
        +" length="+request.getLength());
    }

    // 读取响应
    public Response readResponse() throws IOException {
        Response response=new Response();
        response.setType(dataInputStream.readInt());
        response.setLength(dataInputStream.readInt());
        byte[] payload=new byte[response.getLength()];
        int n=dataInputStream.read(payload);
        if (n != response.getLength()) {
            throw new IOException("读取的响应数据不完整!");
        }
        response.setPayload(payload);
        System.out.println("[Connection] 收到响应! type="+response.getType()
        +" length="+response.getLength());
        return response;
    }

    public Channel createChannel() throws IOException, ClassNotFoundException {
        String channelId="C"+ UUID.randomUUID().toString();
        Channel channel=new Channel(channelId,this);
        // 把这个channel对象放到Connection管理channel的哈希表中
        channelMap.put(channelId,channel);
        // 同时也需要把"创建channel"的这个消息也告诉服务器
        boolean ok=channel.creatChannel();
        if (!ok){
            // 服务器这里创建失败了,整个创建channel的过程不顺利
            // 把刚才已经加入 hash 表里的键值对再删了
            channelMap.remove(channelId);
            return null;
        }
        return channel;
    }
}
